package fybug.nulll.pdstream.WR.Parsing.Source.Partition;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import org.jetbrains.annotations.NonNls;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

import fybug.nulll.pdfunctionlibrary.Processing.Check;
import fybug.nulll.pdfunctionlibrary.Processing.Err.Stop;
import fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys;

import static fybug.nulll.pdstream.WR.Parsing.Source.Partition.Encryption.merge;
/**
 * <h2>文本分割器.</h2>
 * <pre>
 * 根据输入的 <b>标识符</b> 分割文本，支持 <b>多个标识符</b>
 * 可限制 <b>每次缓存大小</b>
 * 每次分割时都会 <b>按照分隔符输入时的顺序</b> 进行检查
 * 检查传入的数据是否含有分隔符,返回的参数交由上层处理
 * 如 <b>达到限制缓存大小</b> 不论是否有分隔符都应 </b>抛出数据<b>
 * <br/>
 * <b>可重新设置最大缓存大小,但不可重设置分隔符</b></pre>
 * <pre>如标识符为
 * <b>{@code "&&","\/","/\"}</b> (输出的样子，就是这里没有转义符)
 * 传入源中的数据为<b>{@code "key=val&&hello\/world/\!"}</b>
 * 则应会分割四次，分割出的数据分别为<b>{@code "key=val","hello","world","!"}</b>
 * </pre>
 *
 * @author fybug
 * @version 0.1.6
 * @see ParsingPartitionS
 * @see ParsingAbstractPartition
 * @since JDK 1.8
 */
//@SuppressWarnings( "all" )
public
class ParsingPartition extends ParsingAbstractPartition {
    private static final long serialVersionUID = 6269764860588051707L;
    // 分隔符
    @Nullable
    @NonNls
    private volatile transient String[] identifier = null;
    // 最大分隔符大小
    private volatile transient int max = 0;

    /**
     * <p>构造一个限制缓存大小的分割器.</p>
     * <pre>
     * 需传入要检查的 <b>分隔符</b>
     * 分隔符可以不止一个
     * <b>可限制最大读取缓存大小</b>
     * </pre>
     *
     * @param mark 分隔符
     * @param size 最大缓存的字符数,如果 小于零 则代表不限制
     *
     * @see #setMaxSize(int)
     */
    public
    ParsingPartition(final int size, @Nullable @NonNls CharSequence... mark)
    {
        mark = Arrarys.checkNullArray(Arrarys.trim(mark));
        if (mark != null) {
            identifier = new String[mark.length];
            @NotNull String s;
            int st = 0; // 非空对象数
            for ( int i = 0; i < mark.length; i++ ){
                /* 寻找最大分隔符 */
                s = mark[i].toString();
                if (s.isEmpty())
                    continue;
                // 比较
                max = Math.max(s.length(), max);
                // 防止空对象
                identifier[st] = s;
                st++;
            }
            if (st < identifier.length)
                identifier = Arrays.copyOf(identifier, st);
            max = max == 0 ? -1 : max;
        } else
            /* 没有分隔符 */
            max = -1;
        setMaxSize(size);
    }

    @Override
    protected
    boolean consistent(@NotNull final Object obj) {
        @NotNull final ParsingPartition partition = (ParsingPartition) obj;
        // 对比内部字段
        boolean b;
        // 对比分隔符
        b = Check.equalsArrays(identifier, partition.identifier);
        // 对比最大大小
        b &= Check.equalsArrayTable(getValue(), partition.getValue());
        return b;
    }

    @Override
    public
    int hashCode()
    { return (getClass().hashCode() + super.hashCode()) << 5; }

    @NotNull
    @Override
    public
    Object clone() {
        return super.clone();
    }

    /**
     * <p>打印出所有分隔符和最大长度.</p>
     * <pre>
     * identifier:{
     *      [分隔符];
     * };
     * maxSize:[0];
     * </pre>
     */
    @NotNull
    @NonNls
    public
    String toString() {
        @NotNull final StringBuilder stringBuffer = new StringBuilder("identifier:{\n");
        @Nullable final String[] identifier = this.identifier;
        @NotNull @NonNls final String sizeString = Integer.toString(size());
        if (identifier != null) {
            int size = 0; // 记录长度
            for ( int i = 0; i < identifier.length; i++ )
                size += identifier[i].length() + 5;
            size += 28;
            size += sizeString.length();
            // 提前缓存
            stringBuffer.ensureCapacity(size);
            for ( int i = 0; i < identifier.length; i++ )
                // 用打印所有分隔符
                stringBuffer.append("\t[").append(identifier[i]).append("];\n");
        }
        stringBuffer.append("};\b\n");
        stringBuffer.append("maxSize:[").append(sizeString).append("];");
        return stringBuffer.toString();
    }

    /*
     * Size
     */

    @Override
    public final
    int lenght() { return max; }

    @Override
    public final
    int size() {
        try {
            return getValue()[0];
        } catch ( Stop e ) {
            return -1;
        }
    }

    /**
     * <p>设置最大缓存大小.</p>
     * <p>除非没有 <b>分隔符</b> 或不限制<br/>
     * 否则 <b>不可以小于</b> 分隔符大小</p>
     *
     * @param size 最大缓存多少个字符,{@code < 0} 则代表不限制
     *
     * @throws IndexOutOfBoundsException 参数错误
     */
    @NotNull
    final public
    ParsingPartition setMaxSize(final int size) {
        if (max != -1 && size > 0 && size < max)
            // 有限制但小于分隔符大小
            throw new IndexOutOfBoundsException(size + "");
        setValue(new int[]{size <= 0 ? -1 : size});
        return this;
    }

    /*
     * Check
     */

    @Override
    public
    long check(final @NotNull String s) {
        @Nullable final String[] identifier = this.identifier; // 分隔符
        if (identifier != null && max != -1) {
            @NotNull String check;
            int index; // 分隔符位置
            for ( int i = 0; i < identifier.length; i++ ){
                check = identifier[i];
                index = s.indexOf(check); // 检查该分隔符的位置
                if (index != -1)
                    return merge(index, check.length());
            }
        }
        // 没找到分隔符或分隔符限制为空
        return -1;
    }

    /*
     * 序列化
     */

    private
    void writeObject(final @NotNull ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(max);
        out.writeObject(getValue());
        out.writeObject(identifier);
    }

    private
    void readObject(@NotNull final ObjectInputStream in) throws IOException, ClassNotFoundException
    {
        in.defaultReadObject();
        max = in.readInt();
        setValue((int[]) in.readObject());
        identifier = (String[]) in.readObject();
    }

    /*
     * 释放
     */

    /**
     * <p>清除该分割器中的最大缓存大小.</p>
     * <p>会将最大缓存大小设置为{@code -1}</p>
     */
    @Override
    public
    void clean() { setMaxSize(-1); }

    /** @see #clean() */
    @Override
    public
    void free() { clean(); }

    /**
     * <p>彻底关闭该分割器.</p>
     * <pre>
     * 清除该分割器中的所有数据
     * 清除后不可再复用
     * </pre>
     */
    @Override
    public
    void close() {
        free();
        super.close();
        identifier = null;
        max = -1;
    }
}
